Micron Document
🎖️GitЯра🎖️

Commit 8f9eec5c0819ec4d9b8a69e6dfc7aaacd86e30a6


Parents : 0632993
Author : James Rich <2199651+jamesarich@users.noreply.github.com>
Signature : Signature validation error
Date : 2026-07-15T17:52:07-05:00
Committer : GitHub <noreply@github.com>
Date : 2026-07-15T22:52:07Z

fix(connections): use a segmented button for transport selection (#6285)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>

Changes
Diff

diff --git a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt
index dc3a0ada77..130d557fc4 100644
--- a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt
+++ b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/component/ConnectionsPreviews.kt
@@ -16,8 +16,11 @@
*/
package org.meshtastic.feature.connections.component
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -133,10 +136,18 @@ fun DeviceSectionHeaderPreview() {
AppTheme { DeviceSectionHeader(title = "Bluetooth Devices", showProgress = true) }
}
+// Bounded width so the reference reflects the full-width segmented control as it renders on the Connections screen —
+// a fillMaxWidth control has no width to fill under the unbounded default preview constraint.
@PreviewLightDark
@Composable
fun TransportSelectorPreview() {
- AppTheme { TransportSelector(activeTransport = DeviceType.BLE, onSelectTransport = {}) }
+ AppTheme {
+ Surface {
+ Box(modifier = Modifier.width(360.dp).padding(16.dp)) {
+ TransportSelector(activeTransport = DeviceType.BLE, onSelectTransport = {})
+ }
+ }
+ }
}
@PreviewLightDark

diff --git a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/TransportSelector.kt b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/TransportSelector.kt
index 31f4ec57df..dd7b2310df 100644
--- a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/TransportSelector.kt
+++ b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/TransportSelector.kt
@@ -16,18 +16,18 @@
*/
package org.meshtastic.feature.connections.ui.components
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.ExperimentalLayoutApi
-import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.FilterChip
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
+import androidx.compose.material3.SegmentedButton
+import androidx.compose.material3.SegmentedButtonDefaults
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
+import androidx.compose.material3.SingleChoiceSegmentedButtonRowScope
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource
import org.meshtastic.core.model.DeviceType
@@ -40,33 +40,41 @@ import org.meshtastic.core.ui.icon.MeshtasticIcons
import org.meshtastic.core.ui.icon.Usb
import org.meshtastic.core.ui.icon.Wifi
-/** Single-choice transport selector rendered below the connection card. */
-@OptIn(ExperimentalLayoutApi::class)
+private const val TRANSPORT_COUNT = 3
+
+/**
+ * Single-choice transport selector rendered below the connection card. A Material 3 [SingleChoiceSegmentedButtonRow]
+ * makes the mutually-exclusive choice explicit: the segments read as one grouped control and the selected transport
+ * shows a check, rather than three independent chips whose filled state was read as "enabled/available" instead of
+ * "selected".
+ */
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TransportSelector(
activeTransport: DeviceType,
onSelectTransport: (DeviceType) -> Unit,
modifier: Modifier = Modifier,
) {
- FlowRow(
- modifier = modifier.fillMaxWidth(),
- horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterHorizontally),
- verticalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- TransportChip(
+ // Fill the width so the control reads as one deliberate group spanning the same width as the connection card
+ // above; each SegmentedButton carries an internal weight(1f), so the three segments divide the row evenly.
+ SingleChoiceSegmentedButtonRow(modifier = modifier.fillMaxWidth()) {
+ TransportSegment(
selected = activeTransport == DeviceType.BLE,
+ index = 0,
label = Res.string.bluetooth,
icon = MeshtasticIcons.Bluetooth,
onClick = { onSelectTransport(DeviceType.BLE) },
)
- TransportChip(
+ TransportSegment(
selected = activeTransport == DeviceType.TCP,
+ index = 1,
label = Res.string.network,
icon = MeshtasticIcons.Wifi,
onClick = { onSelectTransport(DeviceType.TCP) },
)
- TransportChip(
+ TransportSegment(
selected = activeTransport == DeviceType.USB,
+ index = 2,
label = Res.string.usb,
icon = MeshtasticIcons.Usb,
onClick = { onSelectTransport(DeviceType.USB) },
@@ -74,12 +82,32 @@ fun TransportSelector(
}
}
+/**
+ * A single transport segment: shows a check when [selected] and the transport [icon] otherwise, so selection is
+ * unambiguous while the unselected segments still communicate which transport they represent.
+ */
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
-private fun TransportChip(selected: Boolean, label: StringResource, icon: ImageVector, onClick: () -> Unit) {
- FilterChip(
+private fun SingleChoiceSegmentedButtonRowScope.TransportSegment(
+ selected: Boolean,
+ index: Int,
+ label: StringResource,
+ icon: ImageVector,
+ onClick: () -> Unit,
+) {
+ SegmentedButton(
selected = selected,
onClick = onClick,
- label = { Text(stringResource(label)) },
- leadingIcon = { Icon(imageVector = icon, contentDescription = null) },
+ shape = SegmentedButtonDefaults.itemShape(index = index, count = TRANSPORT_COUNT),
+ icon = {
+ SegmentedButtonDefaults.Icon(active = selected) {
+ Icon(
+ imageVector = icon,
+ contentDescription = null,
+ modifier = Modifier.size(SegmentedButtonDefaults.IconSize),
+ )
+ }
+ },
+ label = { Text(text = stringResource(label), maxLines = 1) },
)
}

diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Dark_d19fbf1f_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Dark_d19fbf1f_0.png
index 6de98e712b..9dfec55226 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Dark_d19fbf1f_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Dark_d19fbf1f_0.png differ

diff --git a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Light_b29dc7a7_0.png b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Light_b29dc7a7_0.png
index 8e7d848657..e4a6a9bc43 100644
Binary files a/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Light_b29dc7a7_0.png and b/screenshot-tests/src/screenshotTestDebug/reference/org/meshtastic/screenshots/feature/ConnectionsScreenshotTestsKt/ScreenshotTransportSelector_Light_b29dc7a7_0.png differ

Served by rngit 1.5.2 - Generated in 0.1s